﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Reflection;
using AiFrame.Base.Core.Plugins;
using AiFrame.InterfaceLib.Configuration;
using AiFrame.InterfaceLib.Data.Access;
using AiFrame.InterfaceLib.Data.Access.NHibernateAccess;
using AiFrame.InterfaceLib.Data.Provider;
using AiFrame.InterfaceLib.Logs;
using NHibernate;

namespace AiFrame.Base.Core.Data.Access
{
    /// <summary>
    /// The central database connection.
    /// Can be used through a singleton pattern.
    /// </summary>
    public class DatabaseConnection: IDatabaseConnection, INhibernateHelper
    {
        /// <summary>
        /// The assembly list which elements have to
        /// be registered to NHibernate.
        /// </summary>
        private List<Assembly> _assemblyCollec;

        /// <summary>
        /// The classes list which elements have to
        /// be registered to NHibernate.
        /// </summary>
        private List<Type> _classesCollec;
        private static DatabaseConnection _connection;
        private ISessionFactory _sessionFactory;
        private ISession _session;
        private static INHibernateSessionManager _sessionManager = new DefaultSessionManager();

        private bool _connected = false;

        private bool _loadFromConfigFile = false;

        private string username;

        private string hostAddress;

        private string password;

        private string database;

        private NHibernate.Cfg.Configuration configuration;

        /// <summary>
        /// Sets the session manager
        /// </summary>
        public static INHibernateSessionManager SessionManager
        {
            set{ _sessionManager = value;}
        }

        /// <summary>
        /// Returns the NHibernate session factory.
        /// </summary>
        public ISessionFactory SessionFactory
        {
            get
            {
//                AssemblySettings assemblySettings = new AssemblySettings();

                if (_sessionFactory == null)
                {
                    configuration = new NHibernate.Cfg.Configuration();

                    if (LoadFromConfigFile == false)
                    {
                        #region Connection String
                        string connectionString = String.Format("Data Source={0};Initial Catalog={1}" +
                                                                ";User Id={2};Password={3}", hostAddress, database, username,
                                                                password);

                        // Additional parameters to the connection string
                        if (ProviderRegistration.Instance.Providers.Count > 0 && 
                            String.IsNullOrEmpty(ProviderRegistration.Instance.ActiveProvider.AdditionalParameter) == false)
                        {
                            connectionString += "; " + ProviderRegistration.Instance.ActiveProvider.AdditionalParameter;
                        }

                        // Set the connection string
                        configuration.Configure().SetProperty("connection.connection_string", connectionString);
                        #endregion

//                        LogHandler.GetLogger().Debug("Create Tables: " + assemblySettings["CreateTables"]);
                        
//                        if (bool.Parse(assemblySettings["CreateTables"]) == true)
//                            configuration.SetProperty("hbm2ddl.auto", "create");
//                        else
//                            configuration.SetProperty("hbm2ddl.auto", "none");

                        configuration.SetProperty("prepare_sql", "true");

                        if (ProviderRegistration.Instance.Providers.Count > 0)
                        {
                            IDbProvider dbProvider = ProviderRegistration.Instance.ActiveProvider;
                            configuration.SetProperty("connection.provider", dbProvider.ConnectionProvider);
                            configuration.SetProperty("dialect", dbProvider.Dialect);
                            configuration.SetProperty("connection.driver_class", dbProvider.DriverClass);
                        }
                    }else
                        configuration.Configure();


                    LogHandler.GetLogger().Debug(_assemblyCollec.Count.ToString() + " Assemblies found for NHibernate.");
                    /* Alle Assemblies aus Plugins hinzufügen */
                    foreach (Assembly assembly in _assemblyCollec)
                    {
                        configuration.AddAssembly(assembly);
                    }

                    LogHandler.GetLogger().Debug(_classesCollec.Count.ToString() + " Types found for NHibernate.");
                    foreach (Type type in _classesCollec)
                    {
                        configuration.AddClass(type);
                    }
                    
                    _sessionFactory = configuration.BuildSessionFactory();
                }
                
                return _sessionFactory;
            }
        }

        /// <summary>
        /// Specifies if the database configuration properties
        /// are read from the configuration file.
        /// </summary>
        public bool LoadFromConfigFile
        {
            get { return _loadFromConfigFile; }
            set { _loadFromConfigFile = value; }
        }

        /// <summary>
        /// Get the connection status.
        /// </summary>
        public bool Connected
        {
            get
            {
                return _connected;
            }
            private set
            {
                _connected = value;
                this.OnPropertyChanged("Connection");
            }
        }

        /// <summary>
        /// The private constructor.
        /// </summary>
        private DatabaseConnection()
        {
            _assemblyCollec = new List<Assembly>();
            _classesCollec = new List<Type>();
        }

        /// <summary>
        /// Returns the instance. If there is not any instance, one
        /// will be created.
        /// </summary>
        public static DatabaseConnection Instance
        {
            get
            {
                if (_connection == null)
                {
                    _connection = new DatabaseConnection();
                }
                return _connection;
            }
        }

        /// <summary>
        /// Returns the NHibernate session.
        /// </summary>
        /// <returns>The session.</returns>
        public ISession GetSession()
        {
            try
            {
                ISession session = _sessionManager.GetSession(this);
                IDbConnection conn = null;
                if (Connected == false)
                {
                    conn = session.Connection;
                    Connected = true;
                    PluginManagerLoader.Instance().GetPluginManager().OnConnectionReadyEvent();
                }
                return session;
            }
            catch (Exception e)
            {
                throw new DatabaseConnectionException("Verbindungsfehler", e);
            }

        }

        /// <summary>
        /// Returns the NHibernate configuration.
        /// </summary>
        public NHibernate.Cfg.Configuration Configuration
        {
            get { return configuration; }
        }

        /// <summary>
        /// Closes the session.
        /// </summary>
        public void CloseSession()
        {
            _sessionManager.CloseSession();

            if (_session != null)
            {
                _session.Close();
                _session = null;
            }

            if (_sessionFactory != null)
            {
                _sessionFactory.Close();
                _sessionFactory = null;
            }
        }

        public void AddMappingAssembly(Assembly assembly)
        {            
            _assemblyCollec.Add(assembly);
        }

        public void AddMappingClass(Type type)
        {
            _classesCollec.Add(type);
        }

        public string Username
        {
            get { return username; }
            set { username = value; }
        }

        public string HostAddress
        {
            get { return hostAddress; }
            set { hostAddress = value; }
        }

        public string Password
        {
            set { password = value; }
        }

        public string Database
        {
            get { return database; }
            set { database = value; }
        }

        #region Implementation of INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {

                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}
